var cb
let data = { a: 1, b: { bb: 1 }, c: 1 }

const walk = data => {
  let dep = []
  return new Proxy(data, createHandler(dep))
}

const createHandler = dep => {
  let get = (target, prop, receiver) => {
    let val = Reflect.get(target, prop)

    if (prop === '__dep__') return

    !target.__dep__ && (target.__dep__ = [])
    typeof cb === 'function' && target.__dep__.push(cb)

    return val
  }
  let set = (target, prop, value, receiver) => {
    let val = Reflect.get(target, prop)

    if (value === val || prop === '__dep__') return

    if (Object.prototype.toString.call(value) === '[object Object]') {
      walk(value)
    }
    target.__dep__.forEach(fn => fn())

    return Reflect.set(target, prop, value)
  }
  return { get, set }
}

function $watch(data, key, fn) {
  cb = fn
  let pathArr,
    obj = data
  if (/\./.test(key)) {
    pathArr = key.split('.')
    pathArr.forEach(p => {
      obj = obj[p]
    })
    cb = ''
    return
  }
  data[key]
  cb = ''
}

data = walk(data)

$watch(data, 'c', () => {
  console.log('c_1')
})
$watch(data, 'b', () => {
  console.log('b_1')
})
$watch(data, 'b.bb', () => {
  console.log('bb_1')
})
$watch(data, 'a', () => {
  console.log('a_2')
})
$watch(data, 'a', () => {
  console.log('a_1')
})

// console.dir(data)

// data.c = 1
data.a = 2
// // data.b = { bb: 'b' }
// data.b.bb = '1'
